/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-10
 * V4.0
 */
package com.jphenix.clazz;

import com.jphenix.standard.docs.ClassInfo;


/**
 * An iterator for editing a code attribute.
 *
 * <p>To directly read or edit a bytecode sequence, call {@link #byteAt(int)}, {@link #s16bitAt(int)},
 * {@link #//writeByte(int, int)}, {@link #//write16bit(int, int)}, and other methods.
 * For example, if <code>method</code> refers to a <code>CtMethod</code> object,
 * the following code substitutes the <code>NOP</code> instruction for the first
 * instruction of the method:  
 *
 * <pre>
 * CodeAttribute ca = method.getMethodInfo().getCodeAttribute();
 * CodeIterator ci = ca.iterator();
 * ci.writeByte(Opcode.NOP, 0);</pre>
 *
 * <p>To visit every instruction, call {@link #next()} on a <code>CodeIterator</code>.
 * It returns the index of the first byte of the next instruction.
 *
 * <p>If there are multiple <code>CodeIterator</code>s referring to the
 * same <code>Code_attribute</code>, then inserting a gap by one
 * <code>CodeIterator</code> will break the other
 * <code>CodeIterator</code>.
 *
 * <p>This iterator does not provide <code>remove()</code>.
 * If a piece of code in a <code>Code_attribute</code> is unnecessary,
 * it should be overwritten with <code>NOP</code>.
 *
 * @author mabg
 * @see CodeAttribute#iterator()
 */
@ClassInfo({"2014-06-10 19:50","代码属性信息迭代器"})
public class CodeIterator implements Opcode {
    
    protected CodeAttribute codeAttr;
    protected byte[] bytecode;
    protected int endPos;
    protected int currentPos;
    protected int mark;

    protected CodeIterator(CodeAttribute ca) {
        codeAttr = ca;
        bytecode = ca.getCode();
        begin();
    }

    /**
     * Moves to the first instruction.
     */
    public void begin() {
        currentPos = mark = 0;
        endPos = getCodeLength();
    }

    /**
     * Moves to the given index.
     *
     * <p>The index of the next instruction is set to the given index.
     * The successive call to <code>next()</code>
     * returns the index that has been given to <code>move()</code>.
     *
     * <p>Note that the index is into the byte array returned by
     * <code>get().getCode()</code>.
     *
     * @see CodeAttribute#getCode()
     */
    public void move(int index) {
        currentPos = index;
    }

    /**
     * Gets the index of the position of the mark set by
     * <code>setMark</code>.
     *
     * @return the index of the position.
     * @see #//setMark(int)
     * @since 3.11
     */
    public int getMark() { return mark; }

    /**
     * Returns a Code attribute read with this iterator.
     */
    public CodeAttribute get() {
        return codeAttr;
    }

    /**
     * Returns <code>code_length</code> of <code>Code_attribute</code>.
     */
    public int getCodeLength() {
        return bytecode.length;
    }

    /**
     * Returns the unsigned 8bit value at the given index.
     */
    public int byteAt(int index) { return bytecode[index] & 0xff; }

    /**
     * Returns the unsigned 16bit value at the given index.
     */
    public int u16bitAt(int index) {
        return ByteArray.readU16bit(bytecode, index);
    }

    /**
     * Returns the signed 16bit value at the given index.
     */
    public int s16bitAt(int index) {
        return ByteArray.readS16bit(bytecode, index);
    }


    /**
     * Returns the signed 32bit value at the given index.
     */
    public int s32bitAt(int index) {
        return ByteArray.read32bit(bytecode, index);
    }



    /**
     * Returns true if there is more instructions.
     */
    public boolean hasNext() { return currentPos < endPos; }

    /**
     * Returns the index of the next instruction
     * (not the operand following the current opcode).
     *
     * <p>Note that the index is into the byte array returned by
     * <code>get().getCode()</code>.
     *
     * @see CodeAttribute#getCode()
     * @see CodeIterator#byteAt(int)
     */
    public int next() throws Exception {
        int pos = currentPos;
        currentPos = nextOpcode(bytecode, pos);
        return pos;
    }

    /**
     * Obtains the value that the next call
     * to <code>next()</code> will return.
     *
     * <p>This method is side-effects free.
     * Successive calls to <code>lookAhead()</code> return the
     * same value until <code>next()</code> is called.
     */
    public int lookAhead() {
        return currentPos;
    }

    /**
     * Moves to the instruction for
     * either <code>super()</code> or <code>this()</code>.
     *
     * <p>This method skips all the instructions for computing arguments
     * to <code>super()</code> or <code>this()</code>, which should be
     * placed at the beginning of a constructor body.
     *
     * <p>This method returns the index of INVOKESPECIAL instruction
     * executing <code>super()</code> or <code>this()</code>.
     * A successive call to <code>next()</code> returns the
     * index of the next instruction following that INVOKESPECIAL.
     *
     * <p>This method works only for a constructor.
     *
     * @return  the index of the INVOKESPECIAL instruction, or -1
     *          if a constructor invocation is not found.
     */
    public int skipConstructor() throws Exception {
        return skipSuperConstructor0(-1);
    }

    /**
     * Moves to the instruction for <code>super()</code>.
     *
     * <p>This method skips all the instructions for computing arguments to
     * <code>super()</code>, which should be
     * placed at the beginning of a constructor body.
     *
     * <p>This method returns the index of INVOKESPECIAL instruction
     * executing <code>super()</code>.
     * A successive call to <code>next()</code> returns the
     * index of the next instruction following that INVOKESPECIAL.
     *
     * <p>This method works only for a constructor.
     *
     * @return  the index of the INVOKESPECIAL instruction, or -1
     *          if a super constructor invocation is not found
     *          but <code>this()</code> is found.
     */
    public int skipSuperConstructor() throws Exception {
        return skipSuperConstructor0(0);
    }

    /**
     * Moves to the instruction for <code>this()</code>.
     *
     * <p>This method skips all the instructions for computing arguments to
     * <code>this()</code>, which should be
     * placed at the beginning of a constructor body.
     *
     * <p>This method returns the index of INVOKESPECIAL instruction
     * executing <code>this()</code>.
     * A successive call to <code>next()</code> returns the
     * index of the next instruction following that INVOKESPECIAL.
     *
     * <p>This method works only for a constructor.
     *
     * @return  the index of the INVOKESPECIAL instruction, or -1
     *          if a explicit constructor invocation is not found
     *          but <code>super()</code> is found.
     */
    public int skipThisConstructor() throws Exception {
        return skipSuperConstructor0(1);
    }

    /* skipSuper        1: this(), 0: super(), -1: both.
     */
    private int skipSuperConstructor0(int skipThis) throws Exception {
        begin();
        ConstPool cp = codeAttr.getConstPool();
        String thisClassName = codeAttr.getDeclaringClass();
        int nested = 0;
        while (hasNext()) {
            int index = next();
            int c = byteAt(index);
            if (c == NEW) {
                ++nested;
            } else if (c == INVOKESPECIAL) {
                int mref = ByteArray.readU16bit(bytecode, index + 1);
                if (cp.getMethodrefName(mref).equals(MethodInfo.nameInit)) {
                    if (--nested < 0) {
                        if (skipThis < 0) {
                            return index;
                        }

                        String cname = cp.getMethodrefClassName(mref);
                        if (cname.equals(thisClassName) == (skipThis > 0)) {
                            return index;
                        } else {
                            break;
                        }
                    }
                }
            }
        }

        begin();
        return -1;
    }


    /**
     * An inserted gap.
     *
     * @since 3.11
     */
    public static class Gap {
        /**
         * The position of the gap.
         */
        public int position;

        /**
         * The length of the gap.
         */
        public int length;
    }

    /* opcodeLegth is used for implementing nextOpcode().
     */
    private static final int[] opcodeLength = {
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 2, 3,
        3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1,
        1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
        3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3,
        3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 1, 1, 3, 3, 1, 1, 0, 4, 3, 3,
        5, 5
    };
    // 0 .. LOOKUPSWITCH, TABLESWITCH, WIDE

    /**
     * Calculates the index of the next opcode.
     */
    static int nextOpcode(byte[] code, int index)
        throws Exception
    {
        int opcode;
        try {
            opcode = code[index] & 0xff;
        }
        catch (IndexOutOfBoundsException e) {
            throw new Exception("invalid opcode address");
        }

        try {
            int len = opcodeLength[opcode];
            if (len > 0) {
                return index + len;
            } else if (opcode == WIDE) {
                if (code[index + 1] == (byte)IINC)      // WIDE IINC
                {
                    return index + 6;
                } else {
                    return index + 4;           // WIDE ...
                }
            } else {
                int index2 = (index & ~3) + 8;
                if (opcode == LOOKUPSWITCH) {
                    int npairs = ByteArray.read32bit(code, index2);
                    return index2 + npairs * 8 + 4;
                }
                else if (opcode == TABLESWITCH) {
                    int low = ByteArray.read32bit(code, index2);
                    int high = ByteArray.read32bit(code, index2 + 4);
                    return index2 + (high - low + 1) * 4 + 8;
                }
                // else
                //     throw new BadBytecode(opcode);
            }
        }
        catch (IndexOutOfBoundsException e) {
        }
        // opcode is UNUSED or an IndexOutOfBoundsException was thrown.
        throw new Exception(" opcode is UNUSED or an IndexOutOfBoundsException was thrown:"+opcode);
    }


    static abstract class Branch {
        int pos, orgPos;
        Branch(int p) { pos = orgPos = p; }
        void shift(int where, int gapLength, boolean exclusive) {
            if (where < pos || (where == pos && exclusive)) {
                pos += gapLength;
            }
        }

        static int shiftOffset(int i, int offset, int where,
                               int gapLength, boolean exclusive) {
            int target = i + offset;
            if (i < where) {
                if (where < target || (exclusive && where == target)) {
                    offset += gapLength;
                }
            }
            else if (i == where) {
                // This code is different from the code in CodeIterator#newOffset().
                // see JASSIST-124.
                if (target < where && exclusive) {
                    offset -= gapLength;
                } else if (where < target && !exclusive) {
                    offset += gapLength;
                }
            }
            else
                if (target < where || (!exclusive && where == target)) {
                    offset -= gapLength;
                }

            return offset;
        }

        boolean expanded() { return false; }
        int gapChanged() { return 0; }
        int deltaSize() { return 0; }   // newSize - oldSize

        // This returns the original instruction size.
        abstract int write(int srcPos, byte[] code, int destPos, byte[] newcode) throws Exception;
    }
}
